from typing import Optional
import os
import random
from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QLabel, QLineEdit, QPushButton, QComboBox, QGridLayout, QVBoxLayout, QMessageBox, QProgressBar, QFileDialog
from moviepy.editor import VideoFileClip
import ffmpeg

from app.core.ffmpeg_utils import get_ffmpeg_exe


class ConversionThread(QThread):
    finished = pyqtSignal(bool, str)

    def __init__(self, input_path: str, output_path: str, bitrate: str, format_type: str):
        super().__init__()
        self.input_path = input_path
        self.output_path = output_path
        self.bitrate = bitrate
        self.format_type = format_type.upper()

    def run(self) -> None:
        video = None
        try:
            if not os.path.exists(self.input_path):
                raise FileNotFoundError(f"输入文件 {self.input_path} 不存在")

            video = VideoFileClip(self.input_path)
            audio = video.audio
            if audio is not None:
                codec_map = {
                    'MP3': {'codec': 'libmp3lame', 'params': []},
                    'AAC': {'codec': 'aac', 'params': ['-strict', 'experimental']},
                    'WAV': {'codec': 'pcm_s16le', 'params': ['-ar', '44100', '-ac', '2']},
                    'FLAC': {'codec': 'flac', 'params': ['-compression_level', '12']}
                }
                selected = self.format_type
                audio.write_audiofile(
                    self.output_path,
                    bitrate=self.bitrate if selected not in ['WAV', 'FLAC'] else None,
                    codec=codec_map[selected]['codec'],
                    ffmpeg_params=codec_map[selected]['params'],
                    logger=None,
                )
                self.finished.emit(True, self.output_path)
                return

            # fallback to ffmpeg
            ffmpeg_exe = get_ffmpeg_exe()
            if not ffmpeg_exe:
                raise FileNotFoundError("未找到 ffmpeg，可安装 imageio-ffmpeg 或将 ffmpeg 加入 PATH")

            probe = ffmpeg.probe(self.input_path, cmd=ffmpeg_exe)
            has_audio = any(s.get('codec_type') == 'audio' for s in probe.get('streams', []))
            if not has_audio:
                raise ValueError("该视频不包含音轨，无法提取音频")

            inp = ffmpeg.input(self.input_path)
            selected = self.format_type
            if selected == 'MP3':
                out = ffmpeg.output(inp.audio, self.output_path, acodec='libmp3lame', audio_bitrate=self.bitrate)
            elif selected == 'AAC':
                out = ffmpeg.output(inp.audio, self.output_path, acodec='aac', audio_bitrate=self.bitrate, strict='-2')
            elif selected == 'WAV':
                out = ffmpeg.output(inp.audio, self.output_path, acodec='pcm_s16le', ar=44100, ac=2)
            elif selected == 'FLAC':
                out = ffmpeg.output(inp.audio, self.output_path, acodec='flac', compression_level=12)
            else:
                raise ValueError(f"不支持的格式: {selected}")

            ffmpeg.run(out, overwrite_output=True, cmd=ffmpeg_exe)
            self.finished.emit(True, self.output_path)
        except Exception as exc:
            self.finished.emit(False, str(exc))
        finally:
            if video is not None:
                try:
                    video.close()
                except Exception:
                    pass


class AudioConverterWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._build_ui()
        self._wire()
        self.conversion_thread: Optional[ConversionThread] = None
        self.sim_timer = QTimer(self)
        self.sim_timer.setInterval(150)
        self.sim_timer.timeout.connect(self._on_sim)
        self.simulating = False
        self.actual_done = False

    def _build_ui(self):
        self.input_edit = QLineEdit(self)
        self.input_btn = QPushButton("浏览文件", self)
        self.format_combo = QComboBox(self)
        self.format_combo.addItems(["MP3", "AAC", "WAV", "FLAC"])
        self.format_combo.setCurrentText("MP3")
        self.output_edit = QLineEdit(self)
        self.output_btn = QPushButton("浏览...", self)
        self.bitrate_combo = QComboBox(self)
        self.bitrate_combo.addItems(["128k", "192k", "256k", "320k"])
        self.bitrate_combo.setCurrentText("192k")
        self.convert_btn = QPushButton("开始转换", self)
        self.progress_bar = QProgressBar(self)
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)

        grid = QGridLayout()
        grid.addWidget(QLabel("输入视频路径："), 0, 0)
        grid.addWidget(self.input_edit, 0, 1)
        grid.addWidget(self.input_btn, 0, 2)
        grid.addWidget(QLabel("输出格式:"), 1, 0)
        grid.addWidget(self.format_combo, 1, 1)
        grid.addWidget(QLabel("保存路径:"), 2, 0)
        grid.addWidget(self.output_edit, 2, 1)
        grid.addWidget(self.output_btn, 2, 2)
        grid.addWidget(QLabel("音频质量:"), 3, 0)
        grid.addWidget(self.bitrate_combo, 3, 1)

        layout = QVBoxLayout(self)
        layout.addLayout(grid)
        layout.addWidget(self.progress_bar)
        layout.addWidget(self.convert_btn, 0, Qt.AlignRight)
        self.setLayout(layout)

    def _wire(self):
        self.input_btn.clicked.connect(self._pick_input)
        self.output_btn.clicked.connect(self._pick_output)
        self.format_combo.currentTextChanged.connect(self._update_ext)
        self.convert_btn.clicked.connect(self._start)

    def _pick_input(self):
        path, _ = QFileDialog.getOpenFileName(self, "选择视频文件", "", "视频文件 (*.mp4 *.avi *.mov *.mkv *.flv *.wmv *.webm)")
        if not path:
            return
        self.input_edit.setText(path)
        if not self.output_edit.text().strip():
            base = os.path.splitext(os.path.basename(path))[0]
            out_dir = os.path.dirname(path)
            ext = self.format_combo.currentText().lower()
            self.output_edit.setText(os.path.join(out_dir, f"{base}.{ext}"))

    def _pick_output(self):
        ext = self.format_combo.currentText().lower()
        path, _ = QFileDialog.getSaveFileName(self, "保存音频文件", "", f"{self.format_combo.currentText()} 文件 (*.{ext})")
        if path:
            self.output_edit.setText(path)

    def _update_ext(self):
        if self.input_edit.text().strip() and self.output_edit.text().strip():
            base = os.path.splitext(self.output_edit.text().strip())[0]
            self.output_edit.setText(f"{base}.{self.format_combo.currentText().lower()}")

    def _start(self):
        if self.conversion_thread is not None and self.conversion_thread.isRunning():
            return
        inp = self.input_edit.text().strip()
        outp = self.output_edit.text().strip()
        br = self.bitrate_combo.currentText()
        ft = self.format_combo.currentText()
        if not inp:
            QMessageBox.warning(self, "提示", "请选择输入视频文件")
            return
        if not outp:
            QMessageBox.warning(self, "提示", "请选择输出保存路径")
            return
        self.progress_bar.setValue(0)
        self.convert_btn.setEnabled(False)
        self.simulating = True
        self.actual_done = False
        self.sim_timer.start()
        self.conversion_thread = ConversionThread(inp, outp, br, ft)
        self.conversion_thread.finished.connect(self._done)
        self.conversion_thread.start()

    def _on_sim(self):
        if not self.simulating:
            return
        v = self.progress_bar.value()
        if self.actual_done:
            self.progress_bar.setValue(100)
            self.sim_timer.stop()
            self.simulating = False
            return
        if v < 95:
            self.progress_bar.setValue(min(95, v + random.randint(1, 4)))

    def _done(self, ok: bool, msg: str):
        self.actual_done = True
        if ok:
            QMessageBox.information(self, "成功", f"转换完成！\n输出文件: {msg}")
        else:
            self.simulating = False
            self.sim_timer.stop()
            self.progress_bar.setValue(0)
            QMessageBox.critical(self, "错误", msg or "转换失败")
        self.convert_btn.setEnabled(True)


